/*
 * Copyright (c) 2012, marco.tamburelli@gmail.com
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met: 
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * The views and conclusions contained in the software and documentation are those
 * of the authors and should not be interpreted as representing official policies, 
 * either expressed or implied, of the CometMI Project.
 */
package org.cometmi.gwt.server;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Pattern;

import javassist.NotFoundException;

import org.apache.log4j.Logger;
import org.cometmi.gwt.server.annotation.ClassMiHandler;
import org.cometmi.gwt.server.annotation.ObjectMiHandler;
import org.cometmi.gwt.server.events.MiEvent;
import org.cometmi.gwt.server.events.MiEventHandler;
import org.cometmi.gwt.server.events.QueueEvent;
import org.cometmi.gwt.server.events.QueueEventHandler;
import org.cometmi.gwt.server.impl.ProxyFactoryServer;
import org.cometmi.gwt.server.impl.WrapperFactoryServer;
import org.cometmi.gwt.server.util.ServerUtil;
import org.cometmi.gwt.shared.MiQueue;
import org.cometmi.gwt.shared.MiQueuePool;
import org.cometmi.gwt.shared.exceptions.InvalidArgumentException;
import org.cometmi.gwt.shared.exceptions.NameParseException;
import org.cometmi.gwt.shared.exceptions.NotAllowedQueueException;
import org.cometmi.gwt.shared.request.Call;
import org.cometmi.gwt.shared.request.ConstructorCall;
import org.cometmi.gwt.shared.request.FactoryCall;
import org.cometmi.gwt.shared.request.MethodCall;
import org.cometmi.gwt.shared.request.ptypes.Param;
import org.cometmi.gwt.shared.util.ProxyFactory;
import org.cometmi.gwt.shared.util.WrapperFactory;

import com.google.gwt.user.client.rpc.SerializationException;

/**
 * This class contains method to publish and poll messages from queues.
 * 
 * @author marco.tamburelli@gmail.com
 */
public class MiBox
{
	static protected Logger logger = Logger.getLogger(MiBox.class);

	private Map<String, LogicalQueue> queues;
	private Map<String, List<MiEventHandler>> objMiHandlers;
	private Map<String, List<MiEventHandler>> classMiHandlers;
	private Map<String, List<QueueEventHandler>> queueEventHandlers;

	/* scheduler's fields: */
	private boolean scheduling;
	private Map<String, Long> timerMap;

	protected ProxyFactory proxyFactory;
	protected WrapperFactory wrapperFactory;

	private ExecutorService executor;

	public interface DeferredPoller
	{
		boolean isActive();

		void onPoll(List<Call> elems) throws SerializationException, IOException;
	}

	/**
	 * This class contains only fields providing a description of a queue
	 */
	class QueueDescriptor
	{
		String ownerCredentials;
		Map<String, String> attributes = new HashMap<String, String>();
	}

	/**
	 * This is an utility class. The class defines a blocking queue suitable for
	 * pushing any number of elements and to poll all inserted elements. The
	 * polling operation requires a deferred method which will be invoked only
	 * when there are available elements.
	 */
	class LogicalQueue
	{
		Queue<Call> calls = new LinkedList<Call>();
		Queue<DeferredPoller> pollers = new LinkedList<DeferredPoller>();

		QueueDescriptor descriptor;

		String queueName;

		LogicalQueue(String queueName)
		{
			logger.info("[MiBox.LogicalQueue] - opening queue: " + queueName);

			this.descriptor = new QueueDescriptor();
			this.queueName = queueName;
		}

		synchronized void push(Call call) throws SerializationException, IOException
		{
			this.calls.add(call);

			/*
			 * Publishing methods shouldn't refresh the expiration moment,
			 * because there could be a queue that none listen; such a a queue
			 * should be sooner or later disposed.
			 */
			// registerQueueTimer(this.queueName);

			List<DeferredPoller> pollerList = new ArrayList<DeferredPoller>();

			/*
			 * Every poller still awaiting (active) will be processed. Note that
			 * this operation doesn't alter the structure of the pollers list.
			 */
			for (DeferredPoller p : this.pollers)
				if (p.isActive() && !this.calls.isEmpty()) doPoll(p);

			this.pollers.addAll(pollerList);
		}

		private void doPoll(DeferredPoller poller) throws SerializationException, IOException
		{
			List<Call> callList = new ArrayList<Call>();

			while (!this.calls.isEmpty())
			{
				Call call = this.calls.remove();

				MiHandlerManager miHM = new MiHandlerManager(call, this.queueName);
				miHM.executeOnBefores();

				if (miHM.canPublish())
				{
					callList.add(call);

					miHM.executedOnAfters();
				}
			}

			poller.onPoll(callList);
		}

		synchronized void poll(DeferredPoller poller) throws SerializationException, IOException
		{
			registerQueueTimer(this.queueName);

			/*
			 * The poller is added to the list; we care that the list doesn't
			 * appears empty if ther's a client which keeps polling. The idea is
			 * that when a client makes polling, there should stay at least one
			 * inactive.
			 */
			this.pollers.add(poller);

			/*
			 * If there are no awaiting calls all inactive pollers are
			 * discarder; while if the provided poller is active it will be
			 * processed.
			 */
			if (this.calls.isEmpty())
				while (!this.pollers.isEmpty() && !this.pollers.peek().isActive())
				{
					this.pollers.remove();
					logger.debug("[MiBox.LogicalQueue.poll()] - Discarding a deferred polled ("
							+ poller.getClass().getName() + ") due inactivity on queue " + this.queueName + ".");
				}
			else if (poller.isActive()) doPoll(poller);
		}

		/**
		 * Forces polling for any still active stored poller and clears the
		 * queue descriptor, releasing the queue and making it available for
		 * another user.
		 * 
		 * @throws SerializationException
		 * @throws IOException
		 */
		synchronized void free() throws SerializationException, IOException
		{
			ArrayList<DeferredPoller> currentPollers = new ArrayList<DeferredPoller>();

			synchronized (this)
			{
				DeferredPoller p;
				while ((p = this.pollers.poll()) != null)
					if (p.isActive()) currentPollers.add(p);
			}

			/*
			 * Caching the current poller list is required to avoid unexpected
			 * loops.
			 */
			for (DeferredPoller p : currentPollers)
				doPoll(p);

			this.descriptor = new QueueDescriptor();
		}

		/**
		 * Removes pollers not any more active.
		 * 
		 * This method is designed to be used with a mechanism checking after a
		 * provided amount of time if a queue is still in use: If the queue
		 * seems to be in use (has pollers list not empty) than the poller list
		 * is cleared by not active elements. The next time if the queue is not
		 * in use, the pollers list must be empty.
		 */
		synchronized void clearUnusedPollers()
		{
			Queue<DeferredPoller> activePollers = new LinkedList<DeferredPoller>();

			synchronized (this)
			{
				DeferredPoller p;
				while ((p = this.pollers.poll()) != null)
					if (p.isActive()) activePollers.add(p);
			}

			this.pollers = activePollers;
		}

		/**
		 * returns <code>true</code> if the current user can bind to the queue.
		 * 
		 * The method checks if the queue has credentials, and in case it has if
		 * the poller list if free, because in that case the client is expired
		 * or hover is no longer polling.
		 * 
		 * @return
		 */
		synchronized boolean canBeBound()
		{
			return (this.descriptor.ownerCredentials == null || this.pollers.isEmpty());
		}
	}

	class MiHandlerManager
	{
		private Call call;
		private String queue;

		private boolean canPublish;
		private List<MiEventHandler> matchedHandlers;

		private abstract class MiEvent_ implements MiEvent
		{
			private QueueEvent_ targetQueueEvent;

			MiEvent_()
			{
				LogicalQueue q = addQueue(MiHandlerManager.this.queue);

				/*
				 * Since everything here happens during polling an existing
				 * queue, shouldn't be possible that q is null, we can't however
				 * exclude that in case of unexpected behavior it will happen,
				 * and in case of failure provided queue will be set to null.
				 */
				this.targetQueueEvent = new QueueEvent_(q == null ? null : getQueue(q));
			}

			@SuppressWarnings("unchecked")
			@Override
			public <T> T getMethodArgument(Class<T> clazz, int idx) throws InvalidArgumentException
			{
				List<Param<?>> args;

				if (MiHandlerManager.this.call instanceof MethodCall)
				{
					MethodCall _call = (MethodCall) MiHandlerManager.this.call;
					args = _call.getArguments();
				}
				else if (MiHandlerManager.this.call instanceof FactoryCall)
				{
					FactoryCall _call = (FactoryCall) MiHandlerManager.this.call;
					args = _call.getFactoryMethodArgs();
				}
				else if (MiHandlerManager.this.call instanceof ConstructorCall)
				{
					ConstructorCall _call = (ConstructorCall) MiHandlerManager.this.call;
					args = _call.getConstructorArgs();
				}
				else
					/* Shouldn't be possible */
					return null;

				if (idx >= args.size())
					throw new InvalidArgumentException("Provided argument index exceeds the size of arguments array.");

				Class<?> argClass = args.get(idx).getValue().getClass();

				if (argClass.isAssignableFrom(clazz))
				{
					Param<T> arg = (Param<T>) args.get(idx);
					return arg.getValue();
				}
				else
					throw new InvalidArgumentException("provided " + clazz.getName() + " while " + argClass.getName()
							+ "required ");
			}

			@Override
			public <T> void setMethodArgument(T value, int idx) throws InvalidArgumentException
			{
				Class<?> providedArgType = value.getClass();
				List<Param<?>> args;

				if (MiHandlerManager.this.call instanceof MethodCall)
				{
					MethodCall _call = (MethodCall) MiHandlerManager.this.call;
					args = _call.getArguments();
				}
				else if (MiHandlerManager.this.call instanceof FactoryCall)
				{
					FactoryCall _call = (FactoryCall) MiHandlerManager.this.call;
					args = _call.getFactoryMethodArgs();
				}
				else if (MiHandlerManager.this.call instanceof ConstructorCall)
				{
					ConstructorCall _call = (ConstructorCall) MiHandlerManager.this.call;
					args = _call.getConstructorArgs();
				}
				else
					/* Shouldn't be possible */
					return;

				if (idx >= args.size())
					throw new InvalidArgumentException("Provided argument index exceeds the size of arguments array.");

				Class<?> argClass = args.get(idx).getValue().getClass();

				if (argClass.isAssignableFrom(providedArgType))
				{
					@SuppressWarnings("unchecked")
					Param<T> arg = (Param<T>) args.get(idx);
					arg.setValue(value);
				}
				else
					throw new InvalidArgumentException("provided " + providedArgType.getName() + " while "
							+ argClass.getName() + "required ");
			}

			@Override
			public abstract void setEnabled(boolean enabled);

			@Override
			public abstract boolean isEnabled();

			@Override
			public String getObjectId()
			{
				return MiHandlerManager.this.call.getObjectId();
			}

			@Override
			public QueueEvent getTargetQueueEvent()
			{
				return this.targetQueueEvent;
			}
		}

		MiHandlerManager(Call call, String queue)
		{
			this.call = call;
			this.queue = queue;

			this.canPublish = true;
			this.matchedHandlers = new ArrayList<MiEventHandler>();
		}

		void executeOnBefores()
		{
			if (MiBox.this.objMiHandlers.containsKey(this.call.getObjectId()))
			/*
			 * If there are defined handlers for such object their
			 * onBeforeMethod method will be executed , before pushing the
			 * method ...
			 */
			for (MiEventHandler handler : MiBox.this.objMiHandlers.get(this.call.getObjectId()))
			{
				Class<? extends MiEventHandler> clazz = handler.getClass();
				ObjectMiHandler anno = clazz.getAnnotation(ObjectMiHandler.class);

				boolean allowed = false;

				for (String expr : anno.allowedQueues())
					if (match(this.queue, expr))
					{
						allowed = true;
						break;
					}

				if (!allowed) return;

				executeOnBefores(handler, anno.methodName(), anno.parameterTypes(), clazz);
			}

			String className = this.call.getTargetClassName();

			if (className != null && MiBox.this.classMiHandlers.containsKey(className))
				for (MiEventHandler handler : MiBox.this.classMiHandlers.get(className))
				{
					Class<? extends MiEventHandler> handlerClass = handler.getClass();
					ClassMiHandler anno = handlerClass.getAnnotation(ClassMiHandler.class);

					boolean allowed = false;

					for (String expr : anno.allowedQueues())
						if (match(this.queue, expr))
						{
							allowed = true;
							break;
						}

					if (!allowed) return;

					executeOnBefores(handler, anno.methodName(), anno.parameterTypes(), handlerClass);
				}
		}

		private void executeOnBefores(MiEventHandler handler, String methodName, Class<?>[] argTypes,
				Class<? extends MiEventHandler> clazz)
		{
			String expectedSignature = ServerUtil.getJNISignature(argTypes);
			String callMethod = null;
			String signature = null;

			if (this.call instanceof FactoryCall)
			{
				callMethod = ((FactoryCall) this.call).getFactoryMethod();
				signature = ((FactoryCall) this.call).getArgsSignature();
			}
			if (this.call instanceof MethodCall)
			{
				callMethod = ((MethodCall) this.call).getMethod();
				signature = ((MethodCall) this.call).getArgsSignature();
			}
			if (this.call instanceof ConstructorCall)
			{
				callMethod = ServerUtil.CONSTRUCTOR;
				signature = ((ConstructorCall) this.call).getArgsSignature();
			}

			if (callMethod != null && callMethod.equals(methodName) && expectedSignature.equals(signature))
			{
				MiEvent_ beforeEvent = new MiEvent_()
				{
					private boolean b = true;

					@Override
					public void setEnabled(boolean b)
					{
						this.b = b;
					}

					@Override
					public boolean isEnabled()
					{
						return this.b;
					}
				};

				handler.onBeforeMethod(beforeEvent);
				this.matchedHandlers.add(handler);

				this.canPublish = this.canPublish && beforeEvent.isEnabled();
			}
		}

		boolean canPublish()
		{
			return this.canPublish;
		}

		void executedOnAfters()
		{
			if (!this.canPublish) return;

			/*
			 * ...and in case handlers where found for such object their
			 * onAfterMethod will be executed.
			 */
			for (MiEventHandler handler : this.matchedHandlers)
			{
				MiEvent_ afterEvent = new MiEvent_()
				{
					@Override
					public void setEnabled(boolean enabled)
					{}

					@Override
					public boolean isEnabled()
					{
						return true;
					}
				};

				handler.onAfterMethod(afterEvent);
			}
		}
	}

	private class QueueEvent_ implements QueueEvent
	{
		private MiQueue source;

		QueueEvent_(MiQueue queue)
		{
			this.source = queue;
		}

		@Override
		public MiQueue getSourceQueue() throws NotAllowedQueueException
		{
			if (this.source == null)
				throw new NotAllowedQueueException(
						"The source queue of the event was disposed before the event was created.");
			return this.source;
		}

		@Override
		public MiQueuePool getQueuePool(String queueExpression) throws NameParseException
		{
			if (!Pattern.matches("((\\w+)\\.)*(\\*)", queueExpression))
				throw new NameParseException("Invalid queue expression: " + queueExpression);

			return MiBox.this.getQueuePool(queueExpression);
		}

		@Override
		public MiQueue getQueue(String queueName) throws NameParseException, NotAllowedQueueException
		{
			if (!Pattern.matches("((\\w+)\\.)*(\\w+)", queueName))
				throw new NameParseException("Invalid queue name: " + queueName);

			return MiBox.this.getQueue(queueName);
		}

		@Override
		public boolean isQueueAlive(String queueName) throws NameParseException
		{
			if (!Pattern.matches("((\\w+)\\.)*(\\w+)", queueName))
				throw new NameParseException("Invalid queue name: " + queueName);

			return MiBox.this.isQueueAlive(queueName);
		}
	}

	/**
	 * Default constructor available only for package classes;
	 * 
	 * @throws NotFoundException In case factories can't be created
	 */
	MiBox() throws NotFoundException
	{
		logger.info("[org.cometmi.gwt.server.MiBox] inizializing");

		this.queues = new HashMap<String, LogicalQueue>();
		this.objMiHandlers = new HashMap<String, List<MiEventHandler>>();
		this.classMiHandlers = new HashMap<String, List<MiEventHandler>>();
		this.queueEventHandlers = new HashMap<String, List<QueueEventHandler>>();

		this.wrapperFactory = new WrapperFactoryServer();
		this.proxyFactory = new ProxyFactoryServer();

		this.executor = Executors.newCachedThreadPool();
	}

	void startQueueScheduler()
	{
		if (this.scheduling) return;

		this.scheduling = true;
		this.timerMap = Collections.synchronizedMap(new HashMap<String, Long>());

		this.executor.execute(new Runnable()
		{
			@Override
			public void run()
			{
				logger.info("[MiBox] - starting queue scheduler");

				while (MiBox.this.scheduling)
					try
					{
						LinkedList<String> queues = new LinkedList<String>(MiBox.this.timerMap.keySet());

						if (!queues.isEmpty())
							for (String queue : queues)
								if (System.currentTimeMillis() - MiBox.this.timerMap.get(queue) > 1000 * ServerUtil.QUEUE_EXPIRATION_TIME)
									discardQueue(queue, false);

						Thread.sleep(1000 * ServerUtil.SCHEDULER_FREQUENCE);
					}
					catch (InterruptedException e)
					{}

				logger.info("[MiBox] - ending queue scheduler");
			}
		});
	}

	private void discardQueue(String queueName, boolean force)
	{
		LogicalQueue q = this.queues.get(queueName);

		if (q == null)
			MiBox.this.timerMap.remove(queueName);
		else if (q.canBeBound() || force)
		{
			logger.info("[MiBox] - discarding queue: " + queueName);

			this.queues.remove(queueName);
			MiBox.this.timerMap.remove(queueName);

			/* The event must be fired when the queue is removed */
			searchHandlerForOnDispose(q);
		}
		else
		{
			/*
			 * Before resetting the timer we want to clear the queue by inactive
			 * pollers, in this way, during the next invocation of current
			 * method, if teh queue has no polling client it will be removable.
			 */
			q.clearUnusedPollers();

			registerQueueTimer(queueName);
		}
	}

	void stopQueueScheduler()
	{
		this.scheduling = false;
		this.executor.shutdown();
	}

	/**
	 * Register or updates registration of a queue to the scheduler, for the
	 * queue discard. This method should b e invoked any time some operation on
	 * a queue is performed.
	 * 
	 * @param queue Queue name.
	 */
	private void registerQueueTimer(String queue)
	{
		this.timerMap.put(queue, System.currentTimeMillis());
	}

	/**
	 * Can be published to any number of queues hierarchically dependent.
	 * Publishing to a single defined queue will provoke the creation of it if
	 * it didn't exist yet. Note that only one queue can be created, trying to
	 * publish to multiple queues (an queue expression like
	 * <code>org.test.*</code> will have effect only on existing queues.
	 * 
	 * @param queueExpr The queue name or expression.
	 * @param calls The list of calls to be published.
	 * @throws NameParseException
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 * @throws IOException
	 * @throws SerializationException
	 */
	void publish(final String queueExpr, final List<Call> calls) throws NameParseException, SecurityException,
			NoSuchMethodException, SerializationException, IOException
	{
		if (!Pattern.matches("((\\w+)\\.)*((\\w+)|\\*)", queueExpr))
			throw new NameParseException("Invalid queue expression: " + queueExpr);

		if (queueExpr.endsWith("*"))
		{
			Set<String> keySet = this.queues.keySet();

			for (final String queueName : keySet)
				if (match(queueName, queueExpr)) this.executor.execute(new Runnable()
				{
					@Override
					public void run()
					{
						for (Call call : calls)
							try
							{
								logger.debug("[MiBox.publish()] - pushing a call on queue " + queueName);

								LogicalQueue q = MiBox.this.queues.get(queueName);
								if (q != null) q.push(call.copy());
							}
							catch (Throwable e)
							{
								logger.error("[MiBox.publish()] - error while pushing a call on queue " + queueName, e);
							}
					}
				});
		}
		else
			this.executor.execute(new Runnable()
			{
				@Override
				public void run()
				{
					for (Call call : calls)
						try
						{
							logger.debug("[MiBox.publish()] - pushing a call on queue " + queueExpr);

							LogicalQueue q = addQueue(queueExpr);
							if (q != null) q.push(call.copy());
						}
						catch (Throwable e)
						{
							logger.error("[MiBox.publish()] - error while pushing a call on queue " + queueExpr, e);
						}
				}
			});
	}

	/**
	 * Starts polling an existing queue. Note that the queue must be already
	 * created; if some client tries to poll a not existing queue, Can be polled
	 * only one queue.
	 * 
	 * @param queueName The name of a queue.
	 * @param poller A poller implementation containing deferred operations to
	 *        be performed when elements are available on the queue to be
	 *        polled.
	 * @throws NameParseException
	 * @throws IOException
	 * @throws SerializationException
	 * @throws NotAllowedQueueException In case the queue was never created.
	 */
	void poll(String queueName, DeferredPoller poller) throws NameParseException, SerializationException, IOException,
			NotAllowedQueueException
	{
		if (!Pattern.matches("((\\w+)\\.)*(\\w+)", queueName))
			throw new NameParseException("Invalid queue name: " + queueName);

		LogicalQueue q = this.queues.get(queueName);
		if (q != null)
			q.poll(poller);
		else
			throw new NotAllowedQueueException("Queue " + queueName + " don't exist.");
	}

	/**
	 * Frees a queue whose clients are expected not to poll any longer,
	 * terminating its polling session (if there was one) and releasing the
	 * current user, making it available, if not discarded to another user.
	 * 
	 * @param queueName The name of the queue.
	 * @param discard Flag, that when set to <code>true</code> it will force the
	 *        queue to be discarded firing the
	 *        {@link QueueEventHandler#onDispose(QueueEvent)} event.
	 * @throws NameParseException
	 * @throws SerializationException
	 * @throws IOException
	 */
	void freeQueue(String queueName, boolean discard) throws NameParseException, SerializationException, IOException
	{
		if (!Pattern.matches("((\\w+)\\.)*(\\w+)", queueName))
			throw new NameParseException("Invalid queue name: " + queueName);

		LogicalQueue q = this.queues.get(queueName);

		if (q != null)
		{
			q.free();
			if (discard) discardQueue(queueName, true);
		}
	}

	/**
	 * If doesn't exist yet, adds a queue and starts queue event.
	 * 
	 * @param queueName The name of the queue.
	 * @return Returns the existing queue, or a new queue, or null, if some
	 *         other thread removed the queue before the method returns.
	 */
	LogicalQueue addQueue(String queueName)
	{
		if (!this.queues.containsKey(queueName)) synchronized (this.queues)
		{
			if (!this.queues.containsKey(queueName))
			{
				/*
				 * When a queue is created the timer also should be activated to
				 * enable the scheduler to dispose it after long inactivity.
				 */
				registerQueueTimer(queueName);

				final LogicalQueue q = new LogicalQueue(queueName);
				this.queues.put(queueName, q);

				this.executor.execute(new Runnable()
				{
					@Override
					public void run()
					{
						searchHandlerForOnCreate(q);
					};
				});
			}
		}

		/* At this point could be null */
		return this.queues.get(queueName);
	}

	private void searchHandlerForOnCreate(LogicalQueue q)
	{
		String _queueExpression = q.queueName;

		while (_queueExpression != null)
		{
			if (this.queueEventHandlers.containsKey(_queueExpression))
				for (QueueEventHandler handler : this.queueEventHandlers.get(_queueExpression))
				{
					QueueEvent_ createEvent = new QueueEvent_(getQueue(q));
					handler.onCreate(createEvent);
				}

			_queueExpression = getMatcher(_queueExpression);
		}
	}

	private void searchHandlerForOnUpdate(LogicalQueue q)
	{
		String _queueExpression = q.queueName;

		while (_queueExpression != null)
		{
			if (this.queueEventHandlers.containsKey(_queueExpression))
				for (QueueEventHandler handler : this.queueEventHandlers.get(_queueExpression))
				{
					QueueEvent_ updateEvent = new QueueEvent_(getQueue(q));
					handler.onUpdate(updateEvent);
				}

			_queueExpression = getMatcher(_queueExpression);
		}
	}

	private void searchHandlerForOnDispose(LogicalQueue q)
	{
		String _queueExpression = q.queueName;

		while (_queueExpression != null)
		{
			if (this.queueEventHandlers.containsKey(_queueExpression))
				for (QueueEventHandler handler : this.queueEventHandlers.get(_queueExpression))
				{
					QueueEvent_ disposeEvent = new QueueEvent_(getQueue(q));
					handler.onDispose(disposeEvent);
				}

			_queueExpression = getMatcher(_queueExpression);
		}
	}

	/**
	 * This method returns <code>true</code> if the query with provided name has
	 * been created and it's active.
	 * 
	 * @param queueName The name of a query.
	 * @return
	 */
	boolean isQueueAlive(String queueName) throws NameParseException
	{
		return this.queues.containsKey(queueName);
	}

	/**
	 * This method returns <code>true</code> if the requested queue has not yet
	 * credentials defined, or has defined and are from the same user. Note that
	 * this method, will open a new queue, in case it didn't yet exist.
	 * 
	 * This method can fire 2 events due the possible implicit queue creation
	 * and attributes assignments:
	 * {@link QueueEventHandler#onCreate(QueueEvent)} and
	 * {@link QueueEventHandler#onUpdate(QueueEvent)}
	 * 
	 * @param queueName The name of requested queue.
	 * @param credentials Provided user credentials to check.
	 * @param attributes Queue attributes.
	 * @return
	 */
	boolean setCredentialsForQueue(String queueName, String credentials, Map<String, String> attributes)
	{
		synchronized (this.queues)
		{
			LogicalQueue q = this.queues.get(queueName);

			if (q == null)
			{
				q = new LogicalQueue(queueName);
				this.queues.put(queueName, q);
			}

			if (q.canBeBound())
			{
				q.descriptor.ownerCredentials = credentials;
				final LogicalQueue q1 = q;

				/* Before updating attributes onCreate... */
				this.executor.execute(new Runnable()
				{
					@Override
					public void run()
					{
						searchHandlerForOnCreate(q1);
					}
				});

				if (q.descriptor.attributes.isEmpty() && !attributes.isEmpty())
				{
					q.descriptor.attributes = attributes;

					/* ...and onUpdate after attributes update */
					this.executor.execute(new Runnable()
					{
						@Override
						public void run()
						{
							searchHandlerForOnUpdate(q1);
						}
					});
				}

				return true;
			}
			else
				return q.descriptor.ownerCredentials.equals(credentials);
		}
	}

	/**
	 * This method returns <code>true</code> if the requested queue has not yet
	 * credentials defined, or has defined and are from the same user. Note that
	 * this method, will open a new queue, in case it didn't yet exist.
	 * 
	 * @param queueName The name of requested queue.
	 * @param credentials Provided user credentials to check.
	 * @return
	 */
	boolean setCredentialsForQueue(String queueName, String credentials)
	{
		return setCredentialsForQueue(queueName, credentials, new HashMap<String, String>());
	}

	/**
	 * Sets an attribute value from a queue.
	 * 
	 * Note: this method should be used only for queue we know it's exists and
	 * it's not disposed.
	 * 
	 * @param queueName The name of the target queue.
	 * @param key The attribute key.
	 * @param value The provided attribute value.
	 */
	void setQueueAttribute(String queueName, String key, String value)
	{
		/* The queue must exists so no synchronization should be required */
		LogicalQueue q = this.queues.get(queueName);
		q.descriptor.attributes.put(key, value);

		final LogicalQueue q1 = q;

		this.executor.execute(new Runnable()
		{
			@Override
			public void run()
			{
				searchHandlerForOnUpdate(q1);
			};
		});
	}

	/**
	 * Returns an attribute value from a queue.
	 * 
	 * Note: this method should be used only for queue we know it's exists and
	 * it's not disposed.
	 * 
	 * @param queueName The name of the target queue.
	 * @param key The attribute key.
	 * @return
	 */
	String getQueueAttribute(String queueName, String key)
	{
		/* The queue must exists so no synchronization should be required */
		LogicalQueue q = this.queues.get(queueName);
		return q.descriptor.attributes.get(key);
	}

	/**
	 * Removes an attribute by its key from a queue.
	 * 
	 * Note: this method should be used only for queue we know it's exists and
	 * it's not disposed.
	 * 
	 * @param queueName The name of the target queue.
	 * @param key The attribute key.
	 * @return
	 */
	String removeQueueAttribute(String queueName, String key)
	{
		/* The queue must exists so no synchronization should be required */
		LogicalQueue q = this.queues.get(queueName);
		String value = q.descriptor.attributes.remove(key);

		final LogicalQueue q1 = q;

		this.executor.execute(new Runnable()
		{
			@Override
			public void run()
			{
				searchHandlerForOnUpdate(q1);
			};
		});

		return value;
	}

	/**
	 * Returns an object of type {@link MiQueuePool}.
	 * 
	 * @param queueExpr The expression defining the pool.
	 * @return
	 */
	MiQueuePool getQueuePool(String queueExpr)
	{
		return new MiQueuePoolServer(queueExpr, this);
	}

	private MiQueue getQueue(LogicalQueue q)
	{
		return new MiQueueServer(q, this, ServerUtil.SERVER_CREDENTIALS);
	}

	/**
	 * Returns an object of type {@link MiQueue} providing access to queue
	 * functionality.
	 * 
	 * @param queueName The name of the queue.
	 * @return
	 * @throws NotAllowedQueueException In case the queue is in use by some
	 *         other user than the current (in thsi case current user is teh
	 *         server).
	 */
	MiQueue getQueue(String queueName) throws NotAllowedQueueException
	{
		LogicalQueue q = addQueue(queueName);

		/* Extremely improbable but possible */
		if (q == null)
			throw new NotAllowedQueueException("Requested queue " + queueName + " is not any longer available.");

		return new MiQueueServer(q, this, ServerUtil.SERVER_CREDENTIALS);
	}

	/**
	 * This method add a method invocation handler to an object represented by
	 * its ID.
	 * 
	 * @param objectId &The object Id.
	 * @param handler The handler.
	 */
	void addObjMiHandler(String objectId, MiEventHandler handler)
	{
		if (!this.objMiHandlers.containsKey(objectId))
			this.objMiHandlers.put(objectId, new ArrayList<MiEventHandler>());

		this.objMiHandlers.get(objectId).add(handler);
	}

	/**
	 * This method add a method invocation handler to any object defined by a
	 * provided class name.
	 * 
	 * @param className The class name.
	 * @param handler The handler.
	 */
	void addClassMiHandler(String className, MiEventHandler handler)
	{
		if (!this.classMiHandlers.containsKey(className))
			this.classMiHandlers.put(className, new ArrayList<MiEventHandler>());

		this.classMiHandlers.get(className).add(handler);
	}

	/**
	 * This method register an event handler for a provided queue.
	 * 
	 * @param queueExpression The expression matching one or more queues. The
	 *        queue could not be yet created.
	 * @param qEventHandler The handler.
	 * @throws NameParseException In case the provided queue name is not a
	 *         valid.
	 */
	void addQueueEventHandler(String queueExpression, QueueEventHandler qEventHandler) throws NameParseException
	{
		if (!Pattern.matches("((\\w+)\\.)*((\\w+)|\\*)", queueExpression))
			throw new NameParseException("Invalid queue name: " + queueExpression);

		if (!this.queueEventHandlers.containsKey(queueExpression))
			this.queueEventHandlers.put(queueExpression, new ArrayList<QueueEventHandler>());

		this.queueEventHandlers.get(queueExpression).add(qEventHandler);
	}

	/**
	 * This function test if a provided queue expression match (is sub queue
	 * expression) an expression.
	 * 
	 * @param testExpr The queue expression to examine.
	 * @param cmpExpr The queue expression to compare with.
	 * @return
	 */
	private static boolean match(String testExpr, String cmpExpr)
	{
		char[] _expr = cmpExpr.toCharArray();
		char[] _queue = testExpr.toCharArray();

		if (_expr.length > _queue.length) return false;

		for (int i = 0; i < _expr.length; i++)
		{
			if (i == _expr.length - 1 && _expr[i] == '*') return true;
			if (_expr[i] != _queue[i]) return false;
		}

		return _expr.length == _queue.length;
	}

	private static String getMatcher(String queueName)
	{
		char[] arr = queueName.toCharArray();

		if (arr[0] == '*') return null;

		char[] buff = null;
		boolean foundDot = false;

		for (int i = arr.length - 1; i >= 0; i--)
		{
			if (!foundDot)
			{
				if (arr[i] == '.') foundDot = true;
				if (arr[i] == '*') i--;
			}

			if (foundDot)
			{
				if (buff == null)
				{
					buff = new char[i + 2];
					buff[i + 1] = '*';
				}

				buff[i] = arr[i];
			}
			else if (i == 0)
			{
				buff = new char[1];
				buff[0] = '*';
			}
		}

		return new String(buff);
	}
}
